Dify MCP 集成实验(01):环境地基与首个 MCP Server——MCP 新版 SDK 如何从零跑通?
1. 业务场景
先讲一个我们实际遇到的场景。
一家做客服工单 SaaS 的公司,交付一个「企业级 AI 智能体系统集成」项目:外部系统数据要通过 MCP 进 Dify。交付工程师开工后的第一件事不是写业务逻辑,而是把开发环境搭起来、跑通第一个 server——就像盖楼先打地基。第一个工具选什么?一个「当前时间」工具:记录工单创建时间戳、排障时取基准时间、看板定时任务的调度基准。它无外部依赖、参数少、结果确定,是最简单的真实工具,适合当第一个 server 练手。
我们第一次接这类需求时,第一反应是「装个 SDK 写个 server 能有多难」。真正动手才发现——第一个坑不在业务,在地基:SDK 2.0 把旧 API 整个换掉、Python 3.14 装不上 wheel、Dify 只消费远程 HTTP 端点根本不走 stdio——环境没搭对,后面 02-06 的实验全部白搭。这个实验就是先把地基打牢。
这不是个例。任何「外部系统数据要进 Dify」的集成都是这个模式:Dify 只消费远程 HTTP/SSE 端点(源码确认:无 stdio)——server 必须能部署成可访问的 HTTP 端点,后续 02-06 实验的「Dify 连接」假设才成立。
2. 场景痛点
这个流程的痛点,在起步阶段体现得最直接:
- SDK 版本坑:mcp 2.0.0 是 2026 新版,API
大改——
mcp.server.fastmcp模块不存在,旧教程里的 FastMCP 写法全部过时,照网上教程装直接报错。 - Python 版本不兼容:3.14 装 mcp 有 wheel 兼容风险——环境没搭对,后面所有实验全白搭。
- 部署形态不清:不知道 Dify 只消费远程 HTTP 端点,在 stdio 模式下折腾半天,永远接不进 Dify。
- 端点路径不明:streamable-http 默认端点
/mcp,配置错路径全链路 404——跑通了也连不上。
本质上,环境地基决定了后面 02-06 全部实验能不能跑——地基没打牢,上层全悬空。
3. 方案:为什么是 MCP 官方 SDK + Streamable HTTP
打通 MCP Server 开发环境地基,最直接的路就是官方 SDK 2.0 + Streamable HTTP 部署。
选它的理由:
- 官方 SDK 2.0 一套代码三传输:
MCPServer类 +@server.tool()装饰器 +run(transport=...)——stdio(开发验证)/ streamable-http(部署形态)/ sse 一条代码切换; - Streamable HTTP 是 Dify 接入形态:Dify 只消费远程 HTTP/SSE 端点,server 必须部署成可访问的 HTTP 端点——本实验就是把这个形态跑通;
- 与 106-01 插件同一业务需求:time_tool 插件的需求用 MCP 再实现一遍,天然构成「插件 vs MCP 同需求双实现」的对照起点。
这篇文章我们就用它搭第一个 MCP Server——「当前时间」工具,走完 SDK 安装 → server 结构 → 工具定义 → stdio 本地验证 → Streamable HTTP 部署的全生命周期。
4. 整体架构
链路很清晰:本地开发机(Python venv + server)→ Streamable HTTP 端点 → Dify 服务器(api/web)。关键设计是 stdio 模式先本地验证工具逻辑,再以 streamable-http 部署成 Dify 可访问的端点——先证明工具对,再证明能连。
5. 模块设计
5.1 环境三件套(本实验核心)
# 1. 建 venv(一次):Python 3.11(3.14 装 mcp 有 wheel 兼容风险,106 教训延续)
cd dify-107/tmp && uv venv --python 3.11 venv311
# 2. 装 SDK:官方 mcp 2.0.0(自动带 uvicorn/starlette/anyio)
uv pip install --python venv311 mcp
# 3. 启动 Streamable HTTP 部署(Dify 接入形态)
venv311/Scripts/python server.py http # → http://0.0.0.0:8901/mcp5.2 Server 骨架与工具定义(SDK 2.0 新 API)
from mcp.server.mcpserver import MCPServer
server = MCPServer(
name="dify107_01_time_server",
version="1.0.0",
description="客服工单 SaaS 时间基准工具",
)
@server.tool()
def get_current_time(timezone: str = "local") -> dict:
"""返回当前时间、时区与 UTC 偏移(可选参数 timezone)"""
...
# 三传输:stdio(开发验证)/ streamable-http(部署形态)/ sse
if __name__ == "__main__":
server.run(transport="stdio" if sys.argv[1:] == ["stdio"]
else "streamable-http", host="0.0.0.0", port=8901)注意:mcp 2.0.0 是 2026 新版,API
大改——mcp.server.fastmcp 模块不存在,旧教程里的 FastMCP
写法全部过时;新 API 在 mcp.server.mcpserver(MCPServer 类
+ @server.tool() 装饰器 +
run(transport=...))。
6. 运行验证
| 输入 | 预期 | 结果 |
|---|---|---|
| stdio 调用(UTC+8) | 2026-08-05 20:43:53,offset=+8h |
通过 |
| stdio 调用(UTC-5) | 2026-08-05 07:43:53,offset=-5h(时差正确) |
通过 |
| stdio 调用(UTC+12) | 次日 00:43:53 |
通过 |
| HTTP 端点 tools/list | 返回 get_current_time
工具 |
通过 |
| HTTP 调用(local 默认) | 本地时区 +8h 正确 | 通过 |
| 错误路径(无效参数) | isError=True +
中文错误信息透传 |
通过 |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| mcp 2.0.0 API 大改 | mcp.server.fastmcp
模块不存在,旧教程 FastMCP 写法全报错 |
新 API:MCPServer +
@server.tool() +
run(transport=...)(实测) |
| Python 3.14 不兼容 | 3.14 装 mcp 有 wheel 风险(106 教训延续) | uv venv --python 3.11 +
uv pip install mcp(实测) |
| list_tools 返回结构 | 返回的不是 list 也不是元组,是
ListToolsResult 对象 |
访问
listing.tools;分页字段驼峰
nextCursor(实测) |
| pydantic 字段别名 | 协议 JSON 字段驼峰
isError,写 res.isError 报 AttributeError |
Python 属性访问用
snake_case:res.is_error(实测) |
| 工具错误返回 | server 内 raise ValueError | 客户端收到 isError=True +
"Error executing tool xxx: 信息"(错误信息透传)(实测) |
| 默认端点路径 | run(transport="streamable-http")
默认端点 /mcp |
用 streamable_http_path
参数可改(实测) |
| Windows 路径 | MSYS /d/ 路径在 Windows
程序里报错 |
一律 D:\ 格式(106
教训延续) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-107-01:环境地基与首个MCP Server.md
- Server 源码:server.py | dify107_01_time_server 目录
- 交付验证记录(环境搭建 + 双模式验证 + 对照表初稿):验证记录-01-环境地基与首个MCP Server.md
- 全部目录:dify-107/experiments | dify-107/dsl | dify-107/servers | dify-107/delivery
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。